﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell;

namespace Red.Scripts.Project.Utility
{
    internal class DefinesAccessor
    {
        #region Private Properties

        private System.Reflection.Assembly Assembly { get; set; }

        private System.Reflection.MethodInfo LocatorMethod { get; set; }

        #endregion Private Properties

        #region Public Properties

        public Guid EngineGuid => (Guid)Assembly.GetType( "Red.Scripts.Defines.Guids.Debugger.Engine" ).GetProperty( "Guid" ).GetValue( null );
        public Guid PortSupplierGuid => (Guid)Assembly.GetType( "Red.Scripts.Defines.Guids.Debugger.PortSupplier" ).GetProperty( "Guid" ).GetValue( null );
        public string ActiveEnginePackagePath { get; private set; }
        public string ActiveWorkspaceRoot { get; private set; }

        #endregion Public Properties

        #region Public Methods

        public async Task<bool> Load()
        {
            await System.Threading.Tasks.Task.Run( () =>
            {
                Assembly = LoadAssembly();

                Type t = Assembly.GetType( "Red.Scripts.Defines.Utilities.Locator" );
                LocatorMethod = t.GetMethod( "LocateBasic" );

                ActiveEnginePackagePath = (string)t.GetProperty( "ActiveEnginePackagePath" ).GetValue( null );
                ActiveWorkspaceRoot = (string)t.GetProperty( "ActiveWorkspaceRoot" ).GetValue( null );
            } );

            return true;
        }

        public string Locate( string executable )
        {
            var rawSearchOrder = LoadRawSearchOrder();
            var searchOrder = ConvertSearchOrder( rawSearchOrder );

            return (string)LocatorMethod.Invoke( null, new object[] { executable, searchOrder } );
        }

        #endregion Public Methods

        #region Private Methods

        private System.Reflection.Assembly LoadAssembly()
        {
            const string assemblyFilename = "Red.Scripts.Defines.dll";

            try
            {
                return System.Reflection.Assembly.LoadFrom( assemblyFilename );
            }
            catch ( System.IO.FileNotFoundException )
            {
                string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
                path = System.IO.Path.GetDirectoryName( path );
                path = System.IO.Path.Combine( path, assemblyFilename );
                return System.Reflection.Assembly.LoadFrom( path );
            }
        }

        private IEnumerable<string> LoadRawSearchOrder()
        {
            var settingsManager = new Microsoft.VisualStudio.Shell.Settings.ShellSettingsManager( ServiceProvider.GlobalProvider );
            var userSettingsStore = settingsManager.GetReadOnlySettingsStore( Microsoft.VisualStudio.Settings.SettingsScope.UserSettings );

            Type options = Assembly.GetType( "Red.Scripts.Defines.Constants.Options" );
            Type constants = options.GetNestedType( "SccSearchOrder" );

            string collection = (string)constants.GetField( "Collection" ).GetValue( null );
            string searchOrder = (string)constants.GetField( "Name" ).GetValue( null );

            if ( userSettingsStore.PropertyExists( collection, searchOrder ) )
            {
                string value = userSettingsStore.GetString( collection, searchOrder );
                return value.Split( ';' );
            }

            return null;
        }

        private object ConvertSearchOrder( IEnumerable<string> raw )
        {
            if ( raw == null )
                return null;

            Type sccSearchOrder = Assembly.GetType( "Red.Scripts.Defines.SccSearchOrder" );
            IList searchOrder = Array.CreateInstance( sccSearchOrder, raw.Count() );

            int i = 0;
            foreach ( string item in raw )
            {
                object e = Enum.Parse( sccSearchOrder, item );
                searchOrder[ i ] = e;
                ++i;
            }

            return searchOrder;
        }

        #endregion Private Methods
    }
}
